﻿using Microsoft.Practices.Prism.Commands;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml;
using System.Xml.Serialization;

namespace HobSoft.SoccerBet2014.Client.ViewModels
{
    using Common;
    using Domain.ClientService;
    using dm = Domain.Models;
    [Serializable]
    [XmlRoot("odds-group")]
    public class OddsGroup : CommandViewModel
    {
        #region model events
        public event EventHandler<byte> OnQueryBetInfoBegin;
        public event Action<OddsGroup, BfEvent> OnQueryBfDataOver;
        public event Action<OddsGroup, BetInfo> OnQueryBetInfoOver;
        #endregion

        #region model command
        /// <summary>
        /// 查询必发数据命令
        /// </summary>
        [XmlIgnore]
        public ICommand QueryBfDataCommand
        {
            get;
            protected set;
        }
        /// <summary>
        /// 查询博彩公司投注信息命令
        /// </summary>
        [XmlIgnore]
        public ICommand QueryBetInfoCommand
        {
            get;
            protected set;
        }
        /// <summary>
        /// 查询必发数据以及投注信息命令
        /// </summary>
        [XmlIgnore]
        public ICommand QueryBfAndBetInfoDataCommand { get; protected set; }
        [XmlIgnore]
        public ICommand QueryBfGainCommand { get; protected set; }
        [XmlIgnore]
        public ICommand CancelBfGainCommand { get; protected set; }
        #endregion

        #region cotr & initialize
        private readonly ObservableCollection<Odds> _innerOddsList = new ObservableCollection<Odds>();
        public IBfRequestService BFRequestService;
        public Factory.IViewModelFactory ViewModelFactory;
        public OddsGroup(IBfRequestService _bfRequestService, Factory.IViewModelFactory _viewModelFactory)
        {
            BFRequestService = _bfRequestService;
            ViewModelFactory = _viewModelFactory;
            InitializeCommand();
        }

        protected void InitializeCommand()
        {
            this.QueryBfDataCommand = new DelegateCommand(QueryBfData);
            this.QueryBetInfoCommand = new DelegateCommand<BetItem?>(QueryBetInfo);
            this.QueryBfAndBetInfoDataCommand = new DelegateCommand(QueryBfAndBetInfoData);
            this.QueryBfGainCommand = new DelegateCommand(QueryBalance);
            this.CancelBfGainCommand = new DelegateCommand(CancelBalance);
            RegisterCommand(QueryBetInfoCommand, "查询BetInfo");
            RegisterCommand(QueryBfDataCommand, "查询Bf数据");
            RegisterCommand(QueryBfAndBetInfoDataCommand, "查询BetInfo和Bf数据");

            this.PropertyChanged += OnPropertyChanged;
            this.OddsList = new ReadOnlyObservableCollection<Odds>(_innerOddsList);
        }

        #endregion

        #region protected method
        public void ResetMaxPrice()
        {
            var ibcHPrice = IbcOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(IbcOdds.HValue);
            var sboHPrice = SboOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(SboOdds.HValue);
            var hgHPrice = HgOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(HgOdds.HValue);
            MaxH = Odds.PriceValue.Max(ibcHPrice, sboHPrice, hgHPrice).MSPrice;

            var ibcAPrice = IbcOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(IbcOdds.AValue);
            var sboAPrice = SboOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(SboOdds.AValue);
            var hgAPrice = HgOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(HgOdds.AValue);
            MaxA = Odds.PriceValue.Max(ibcAPrice, sboAPrice, hgAPrice).MSPrice;

            var ibcOPrice = IbcOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(IbcOdds.OValue);
            var sboOPrice = SboOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(SboOdds.OValue);
            var hgOPrice = HgOdds == null ? Odds.PriceValue.CreateFromMS(0.0m) : Odds.PriceValue.CreateFromMS(HgOdds.OValue);
            MaxO = Odds.PriceValue.Max(ibcOPrice, sboOPrice, hgOPrice).MSPrice;
        }

        protected void InitiaOdds(ref Odds odds)
        {
            if (odds != null)
            {
                odds.Game = Game;
                odds.OnQueryBetInfoOver += OnOddsQueryBetInfoOver;
                odds.OnQueryBetInfoBegin += OnOddsQueryBetInfoBegin;
            }
        }

        private object _syncOddsLock = new object();
        protected void ResetOddsCollection(Odds newo, Odds old)
        {
            if (old == null && newo == null) return;
            if (old != newo)
            {
                lock (_syncOddsLock)
                {
                    if (old != null && _innerOddsList.Contains(old)) _innerOddsList.Remove(old);
                    if (newo != null && !_innerOddsList.Contains(newo)) _innerOddsList.Add(newo);
                }
            }
            else
            {
                lock (_syncOddsLock)
                {
                    if (newo != null && !_innerOddsList.Contains(newo)) _innerOddsList.Add(newo);
                }
            }
        }
        #endregion

        #region command imple
        protected void QueryBfData()
        {
            if (BFRequestService == null)
            {
                PublicLog.WarnFormat("未初始化bf请求服务，需要BFRequestService");
                return;
            }
            RaiseCommandStart(QueryBfDataCommand, "开始查询必发数据");
            BFRequestService.GetEventMarkets(BfEventID).ContinueWith(t =>
            {
                BetApplication.Current.Dispatcher.Invoke(new Action(delegate()
                {
                    RaiseCommandOver(QueryBfDataCommand);
                    if (t.Exception != null)
                    {
                        RaiseCommandError(t.Exception);
                        PublicLog.ErrorFormat("请求必发赔率数据发生错误：{0}", t.Exception.InnerException.Message);
                        return;
                    }
                    var dmevent = ((Task<dm.BFEvent>)t).Result;
                    if (dmevent == null) return;
                    var vmevent = ViewModelFactory.CreateOrGet<BfEvent>();
                    AutoMapper.Mapper.Map(dmevent, vmevent);
                    var marketsQuery = dmevent.Markets.OrderBy(m => m.CategoryID);
                    foreach (var mk in marketsQuery)
                    {
                        var vmMarket = ViewModelFactory.CreateOrGet<BfMarket>();
                        AutoMapper.Mapper.Map(mk, vmMarket);

                        vmevent.MarketModels.Add(vmMarket);
                    }
                    vmevent.BFRequestService = BFRequestService;
                    vmevent.Game = Game;
                    RaiseOnQueryBfDataOver(vmevent);
                }));
            });
        }

        protected void QueryBetInfo(BetItem? betTo)
        {
            if (!betTo.HasValue) return;
            if (_innerOddsList.Count == 0) return;
            RaiseCommandStart(QueryBetInfoCommand, "开始查询BetInfo");
            var query = _innerOddsList.Where(o => o.LocalizationOdds);
            bool isFocus = true;
            foreach (var odds in query)
            {
                RaiseOnQueryBetInfoBegin(odds.BmID);
                odds.QueryBetInfo(betTo.Value, isFocus);
                isFocus = false;
            }
            RaiseCommandOver(QueryBetInfoCommand);
        }

        protected void QueryBfAndBetInfoData()
        {
            QueryBfData();
            QueryBetInfo(BetItem.HomeOrOver);
        }

        public void QueryBalance()
        {
            Balance = new BfBalance();
            Balance.HomeTeam = Game.HomeTeam;
            Balance.AwayTeam = Game.AwayTeam;
            Balance.Query();
        }

        public void CancelBalance()
        {
            Balance = null;
        }
        #endregion

        #region events handler
        protected void OnOddsQueryBetInfoBegin(Odds sender, BetItem betTo)
        {
            var query = _innerOddsList.Where(o => o != sender);
            foreach (var odds in query)
            {
                RaiseOnQueryBetInfoBegin(odds.BmID);
                odds.QueryBetInfo(betTo, false);
            }
            QueryBfData(); 
        }

        protected void OnOddsQueryBetInfoOver(Odds sender, BetInfo bif)
        {
            RaiseOnQueryBetInfoOver(bif);
        }

        protected void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "IbcOdds":
                    InitiaOdds(ref _ibcOdds);
                    break;
                case "SboOdds":
                    InitiaOdds(ref _sboOdds);
                    break;
                case "HgOdds":
                    InitiaOdds(ref _hgOdds);
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region raise events
        protected void RaiseOnQueryBetInfoBegin(byte bmId)
        {
            if (OnQueryBetInfoBegin != null)
                OnQueryBetInfoBegin(this, bmId);
        }

        protected void RaiseOnQueryBfDataOver(BfEvent eventData)
        {
            if (OnQueryBfDataOver != null)
                OnQueryBfDataOver(this, eventData);
        }

        protected void RaiseOnQueryBetInfoOver(BetInfo bif)
        {
            if (OnQueryBetInfoOver != null)
            {
                OnQueryBetInfoOver(this, bif);
            }
        }
        #endregion

        private long _version;
        [XmlElement("version")]
        public long Version
        {
            get { return _version; }
            set
            {
                PropertyValueChange(ref _version, value, "Version");
            }
        }

        private bool _isFilter;
        [XmlIgnore]
        public bool IsFilter
        {
            get { return _isFilter; }
            set
            {
                PropertyValueChange(ref _isFilter, value, "IsFilter");
            }
        }

        #region property
        private int _bBfEventID;
        [XmlAttribute("bf-id")]
        public int BfEventID
        {
            get { return _bBfEventID; }
            set
            {
                PropertyValueChange(ref _bBfEventID, value, "BfEventID");
            }
        }

        private BfBalance _balance;
        [XmlIgnore]
        public BfBalance Balance
        {
            get { return _balance; }
            set
            {
                PropertyValueChange(ref _balance, value, "Balance");
            }
        }

        private Game _game;
        [XmlElement("game")]
        public Game Game
        {
            get { return _game; }
            set
            {
                PropertyValueChange(ref _game, value, "Game");
            }
        }

        private BfEvent _bfEvent;
        [XmlElement("bf-event")]
        public BfEvent BfEvent
        {
            get { return _bfEvent; }
            set
            {
                PropertyValueChange(ref _bfEvent, value, "BfEvent");
            }
        }

        private dm.Odds.MarketType _type;
        [XmlAttribute("type")]
        public dm.Odds.MarketType Type
        {
            get { return _type; }
            set
            {
                PropertyValueChange(ref _type, value, "Type");
            }
        }

        private decimal _handicap;
        [XmlAttribute("handicap")]
        public decimal Handicap
        {
            get { return _handicap; }
            set
            {
                PropertyValueChange(ref _handicap, value, "Handicap");
            }
        }

        private Odds _ibcOdds;
        [XmlElement("ibc-odds")]
        public Odds IbcOdds
        {
            get { return _ibcOdds; }
            set
            {
                ResetOddsCollection(value, _ibcOdds);
                PropertyValueChange(ref _ibcOdds, value, "IbcOdds");
            }
        }

        private Odds _sboOdds;
        [XmlElement("sbo-odds")]
        public Odds SboOdds
        {
            get { return _sboOdds; }
            set
            {
                ResetOddsCollection(value, _sboOdds);
                PropertyValueChange(ref _sboOdds, value, "SboOdds");
            }
        }

        private Odds _hgOdds;
        [XmlElement("hg-odds")]
        public Odds HgOdds
        {
            get { return _hgOdds; }
            set
            {
                ResetOddsCollection(value, _hgOdds);
                PropertyValueChange(ref _hgOdds, value, "HgOdds");
            }
        }

        private decimal _maxH;
        [XmlIgnore]
        public decimal MaxH
        {
            get { return _maxH; }
            set
            {
                PropertyValueChange(ref _maxH, value, "MaxH");
            }
        }

        private decimal _maxA;
        [XmlIgnore]
        public decimal MaxA
        {
            get { return _maxA; }
            set
            {
                PropertyValueChange(ref _maxA, value, "MaxA");
            }
        }

        private decimal _maxO;
        [XmlIgnore]
        public decimal MaxO
        {
            get { return _maxO; }
            set
            {
                PropertyValueChange(ref _maxO, value, "MaxO");
            }
        }

        private bool _isLive;
        [XmlAttribute("live")]
        public bool IsLive
        {
            get { return _isLive; }
            set
            {
                PropertyValueChange(ref _isLive, value, "IsLive");
            }
        }

        [XmlIgnore]
        public long SortIndex { get; set; }

        private ReadOnlyObservableCollection<Odds> _oddsList;
        public ReadOnlyObservableCollection<Odds> OddsList
        {
            get { return _oddsList; }
            set
            {

                PropertyValueChange(ref _oddsList, value, "OddsList");
            }
        }

        #endregion
    }
}
